package com.scau.os.course_manager.Clock;

import com.scau.os.deviceManagement.DeviceExecute;
import com.scau.os.extension.EventSystem;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TimeManager {
    private IntegerProperty curSystemTime=new SimpleIntegerProperty();
    //相对时钟
    private IntegerProperty relativeTime=new SimpleIntegerProperty();
    private List<Timeout> tasks=new LinkedList<>();

    private List<Runnable>tasksToUnRegister=new ArrayList<>();
    private List<Timeout> timeoutsToAdd=new ArrayList<>();
    private int systemClock=0;

    private static final TimeManager timeManager=new TimeManager();

    public static TimeManager getInstance(){
        return timeManager;
    }
    public ScheduledExecutorService executor;

    public TimeManager() {
        executor = Executors.newScheduledThreadPool(1);
        Runnable task = new Runnable() {
            @Override
            public void run() {
                /**
                 * 这里如果将DeviceExecute.executePeripheral()放在update()方法的后面会导致设备时间比预想的少一个
                 * 因为在update()方法里面更新完设备使用情况之后，便会立刻调用DeviceExecute.executePeripheral()方法把时间-1然后
                 * 刷新显示，所以会少一秒
                 */
                DeviceExecute.executePeripheral();
                //System.out.println("1");
                update();
                //DeviceExecute.executePeripheral();
                EventSystem.instance.SendEvent("SystemClockUpdate",systemClock,false);

                systemClock++;
            }
        };

        // 每隔1秒执行一次任务
        executor.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
    }

    /**
     * 给方法加上synchronized关键字后，会为方法使用的所有对象都上锁
     * 这可能导致死锁问题。比如我在下面的addDelayTime方法加上synchronized关键字
     * 会导致它们两个方法进行资源竞争
     */
    private  void update(){
        if(tasksToUnRegister.size()!=0){
            for (Runnable r :
                    tasksToUnRegister) {
                r.run();
            }
            tasksToUnRegister.clear();
        }
        if(timeoutsToAdd.size()!=0){
            for (Timeout t :
                    timeoutsToAdd) {
                tasks.add(t);
            }
            timeoutsToAdd.clear();
        }
        for (Timeout t :
                tasks) {
            t.update();
        }
        if(timeoutsToAdd.size()!=0){
            for (Timeout t :
                    timeoutsToAdd) {
                tasks.add(t);
            }
            timeoutsToAdd.clear();
        }
        if(tasksToUnRegister.size()!=0){
            for (Runnable r :
                    tasksToUnRegister) {
                r.run();
            }
            tasksToUnRegister.clear();
        }
    }

    public void addDelayTime(Runnable task,int delayTime){
        Timeout t=new Timeout(delayTime,task);
        timeoutsToAdd.add(t);
        t.setFinishCallback(new Runnable() {
            @Override
            public void run() {
                tasksToUnRegister.add(new Runnable() {
                    @Override
                    public void run() {
                        tasks.remove(t);
                    }
                });
            }
        });
    }
    public  void addDelayTime(Runnable task,int delayTime,int intervalTime,Runnable intervalTask){
        Timeout t=new Timeout(delayTime,intervalTime,intervalTask,task);
        timeoutsToAdd.add(t);
        t.setFinishCallback(new Runnable() {
            @Override
            public void run() {
                tasksToUnRegister.add(new Runnable() {
                    @Override
                    public void run() {
                        tasks.remove(t);
                    }
                });
            }
        });
    }
}
